/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2014-2019.
 * Description: set kbox region
 * Author: lichaoyue
 * Create: 2014-3-24
 */

#include <linux/sched.h>
#include <linux/hardirq.h>
#include <linux/set_kbox_region.h>
#include <asm/current.h>
#include <asm/atomic.h>
#include <linux/task_struct_extend.h>

/*
 * kbox_region_conf
 *
 * |__context__|__nesting_level__|__region_symbol__|
 *     3byte         5byte             8byte
 */


/*
 * using by kernel where write data to kbox
 */
int kern_kbox_write(int fd, const char *buf, unsigned len)
{
	return kbox_write(fd, buf, len);
}

#ifndef CONFIG_RTOS_KBOX_MINI
/*
 * using by kernel where register a region from kbox
 */
int kern_kbox_register_region(const char *name, size_t size, struct module *mod)
{
	struct kbox_region region;

	strncpy(region.name, name, KBOX_REGION_NAME_LEN);
	region.name[KBOX_REGION_NAME_LEN - 1] = '\0';
	region.size = size;
	region.mod = mod;

	return kbox_create_region(&region);
}

/*
 * use the region symbol(defined in a enumerate) and return kbox_region_conf
 */
static int make_region_config(int symbol)
{
	int config_ret;

	if (symbol < 0)
		symbol = KBOX_DEFAULT_REG_ID;

	config_ret = symbol;

	if (in_irq()) {
		config_ret |= HARDIRQ_CONTEXT;
		config_ret |= preempt_count() & HARDIRQ_MASK;
	} else if (in_softirq()) {
		config_ret |= SOFTIRQ_CONTEXT;
	} else {
		config_ret |= PROCESS_CONTEXT;
	}

	return config_ret;
}

/*
 * initialize kbox_region_conf as the process context with panic region state
 */
void init_kbox_region(struct task_struct *p)
{
	struct rtos_task_struct *rtos_task = task_to_rtos_task(p);

	atomic_set(&rtos_task->kbox_region_conf, PROCESS_CONTEXT | KBOX_DEFAULT_REG_ID);
}

/*
 * refresh the kbox_region_conf setting, and return the old setting value
 */
int set_kbox_region(int mod_region)
{
	struct rtos_task_struct *rtos_task = task_to_rtos_task(current);
	int new_setting = make_region_config(mod_region);
	int old_setting = atomic_read(&rtos_task->kbox_region_conf);

	if (mod_region < 0)
		new_setting = make_region_config(KBOX_DEFAULT_REG_ID);

	atomic_set(&rtos_task->kbox_region_conf, new_setting);

	return old_setting;
}
EXPORT_SYMBOL(set_kbox_region);

void restore_kbox_region(int old_setting)
{
	struct rtos_task_struct *rtos_task = task_to_rtos_task(current);

	if (((old_setting) < 0) ||
	    ((old_setting & CONTEXT_MASK) > HARDIRQ_CONTEXT))
		old_setting = make_region_config(KBOX_DEFAULT_REG_ID);

	atomic_set(&rtos_task->kbox_region_conf, old_setting);
}
EXPORT_SYMBOL(restore_kbox_region);

int get_kbox_region(void)
{
	struct rtos_task_struct *rtos_task = task_to_rtos_task(current);
	int setting = atomic_read(&rtos_task->kbox_region_conf);
	int cur_setting = make_region_config(KBOX_DEFAULT_REG_ID);

	if ((setting & CONTEXT_LEVEL_MASK) == (cur_setting & CONTEXT_LEVEL_MASK))
		return setting & REGIONID_MASK;

	return KBOX_DEFAULT_REG_ID;
}
EXPORT_SYMBOL(get_kbox_region);

#endif
